use std::fmt;
use crate::protocol::parsing::parse_abs_type;

#[derive(Debug, Clone, PartialEq)]
pub enum TypeSchema {
    Void,
    Int,
    Float,
    Str,
    Bool,
    Json,
    List(Box<TypeSchema>),
    Map(Box<TypeSchema>, Box<TypeSchema>),
    Tuple(Vec<TypeSchema>),
    Opt(Box<TypeSchema>),
    DataType(String),
}

impl serde::Serialize for TypeSchema {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer {
        serializer.serialize_str(format!("{}", self).as_str())
    }
}

impl TypeSchema {
    pub fn from_str(s: &str) -> Self {
        parse_abs_type(s).unwrap()
    }
    pub fn short_str(&self) -> String {
        match self {
            Self::Void => "Void".to_string(),
            Self::Int => "Int".to_string(),
            Self::Float => "Float".to_string(),
            Self::Str => "Str".to_string(),
            Self::Bool => "Bool".to_string(),
            Self::Json => "Json".to_string(),
            Self::List(inner) => format!("[{}]", inner.short_str()),
            Self::Map(key, value) => format!("Map<{}, {}>", key.short_str(), value.short_str()),
            Self::Tuple(inner) => format!("({})", inner.iter().map(|t| t.short_str()).collect::<Vec<String>>().join(", ")),
            Self::Opt(inner) => format!("?{}", inner.short_str()),
            Self::DataType(name) => name.clone(),
        }
    }
}

impl fmt::Display for TypeSchema {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TypeSchema::Void => write!(f, "Void"),
            TypeSchema::Int => write!(f, "Int"),
            TypeSchema::Float => write!(f, "Float"),
            TypeSchema::Str => write!(f, "String"),
            TypeSchema::Bool => write!(f, "Bool"),
            TypeSchema::Json => write!(f, "Json"),
            TypeSchema::List(inner) => write!(f, "List<{}>", inner),
            TypeSchema::Map(key, value) => write!(f, "Map<{}, {}>", key, value),
            TypeSchema::Tuple(inner) => write!(f, "({})", inner.iter().map(|t| t.to_string()).collect::<Vec<String>>().join(", ")),
            TypeSchema::Opt(inner) => write!(f, "?{}", inner),
            TypeSchema::DataType(name) => write!(f, "{}", name),
        }
    }
}


